#include "intrp.h"
#include "helper.h"
#include "exec.h"

target_size_t
intrp_lc3_exec (MachineStatus *ms, void *start, void *end) {
    target_size_t *inst_ptr, *inst_start, *inst_end;
    target_size_t *registers = ms->registers;
    inst_start = start;
    inst_end = end;
    #define INST (*inst_ptr)
    for (inst_ptr = inst_start; inst_ptr < inst_end; inst_ptr++) {
        read_memory (ms, REG (R_PC), &(REG (R_IR)));
        REG (R_PC) = (REG (R_PC) + 1) & 0xFFFF;
        DEF_INST (ADD, INST, 0xF038, 0x1000, {
            REG (DR) = (REG (SR1) + REG (SR2)) & 0xFFFF;
            SET_FLAGS ();
        }); 

        DEF_INST (ADDI,INST, 0xF020, 0x1020, {
            REG (DR) = (REG (SR1) + imm5) & 0xFFFF;
            SET_FLAGS ();
        });

        DEF_INST (AND, INST,0xF038, 0x5000, {
            REG (DR) = REG (SR1) & REG (SR2);
            SET_FLAGS ();
        });

        DEF_INST (ANDI,INST, 0xF020, 0x5020, {
            REG (DR) = REG (SR1) & imm5;
            SET_FLAGS ();
        });

        DEF_INST (BR, INST,0xF000, 0x0000, {
            if ((REG (R_PSR) & FLAGS) != 0)
            REG (R_PC) = (REG (R_PC) + imm9) & 0xFFFF;
        });

        DEF_INST (JMP, INST,0xFE3F, 0xC000, {
            REG (R_PC) = REG (BaseR);
        });

        DEF_INST (JSR, INST,0xF800, 0x4800, {
            REG (R_R7) = REG (R_PC);
            REG (R_PC) = (REG (R_PC) + imm11) & 0xFFFF;
        });

        DEF_INST (JSRR, INST,0xFE3F, 0x4000, {
            target_size_t tmp = REG (BaseR);
            REG (R_R7) = REG (R_PC);
            REG (R_PC) = tmp;
        });

        DEF_INST (LD, INST,0xF000, 0x2000, {
             read_memory (ms, (REG (R_PC) + imm9) & 0xFFFF, &(REG (DR)));
            SET_FLAGS ();
        });

        DEF_INST (LDI,INST, 0xF000, 0xA000, {
            target_size_t tmp;
             read_memory (ms, (REG (R_PC) + imm9) & 0xFFFF, &tmp);
             read_memory (ms, tmp, &(REG (DR)));
            SET_FLAGS ();
        });

        DEF_INST (LDR,INST, 0xF000, 0x6000, {
             read_memory (ms, (REG (BaseR) + imm6) & 0xFFFF, &(REG (DR)));
            SET_FLAGS ();
        });

        DEF_INST (LEA,INST, 0xF000, 0xE000, {
            REG (DR) = (REG (R_PC) + imm9) & 0xFFFF;
            SET_FLAGS ();
        });

        DEF_INST (NOT, INST,0xF03F, 0x903F, {
            REG (DR) = (REG (SR1) ^ 0xFFFF);
            SET_FLAGS ();
        }); 

        DEF_INST (RTI,INST, 0xFFFF, 0x8000, {
        });
        
        DEF_INST (ST,INST, 0xF000, 0x3000, {
             write_memory (ms, (REG (R_PC) + imm9) & 0xFFFF, REG (SR));
        });

        DEF_INST (STI,INST, 0xF000, 0xB000, {
            target_size_t tmp;
            read_memory (ms, (REG (R_PC) + imm9) & 0xFFFF, &tmp);
            write_memory (ms, tmp, REG (SR));
        });

        DEF_INST (STR,INST, 0xF000, 0x7000, {
             write_memory (ms, (REG (BaseR) + imm6) & 0xFFFF, REG (SR));
        });

        DEF_INST (TRAP,INST, 0xFF00, 0xF000, {
            REG (R_R7) = REG (R_PC);
             read_memory (ms, vec8, &(REG (R_PC)));
        });
        executed:
    }
    return REG (R_PC);
}